﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Qiang.Topology.Geometries;
using Qiang.QMath;


namespace Qiang.Data
{
    [Serializable]
    public class Extent:IExtent
    {

        public Extent()
        {
            this._minX = double.MaxValue;
            this._minY = double.MaxValue;
            this._maxX = double.MinValue;
            this._maxY = double.MinValue;
        }

        public Extent(Coordinate coor)
        {
            _minX = coor.X;
            _minY = coor.Y;
            _maxX = coor.X;
            _maxY = coor.Y;
        }

        public Extent(double xMin, double yMin, double xMax, double yMax)
        {
            this._minX = xMin;
            this._minY = yMin;
            this._maxX = xMax;
            this._maxY = yMax;
        }


        # region 属性

        private double _minX;

        public double MinX
        {
            get { return _minX; }
            set { _minX = value; }
        }

        private double _maxX;

        public double MaxX
        {
            get { return _maxX; }
            set { _maxX = value; }
        }

        private double _minY;

        public double MinY
        {
            get { return _minY; }
            set { _minY = value; }
        }

        private double _maxY;

        public double MaxY
        {
            get { return _maxY; }
            set { _maxY = value; }
        }


        public double Width
        {
            get
            {
                return this._maxX - this._minX;
            }
            set
            {
                this._maxX = this._minX + value;
            }
        }

        public double Height
        {
            get { return this._maxY - this._minY; }
            set { this._maxY = this._minY + value; }
        }


        public Coordinate Center
        {
            get
            {
                double x = this._minX + (this._maxX - this._minX) / 2;
                double y = this._minY + (this._maxY - this._minY) / 2;

                return new Coordinate(x, y);
            }
        }

        
        public bool IsEmpty
        {
            get
            {
                if (double.IsNaN(this.MinX) || double.IsNaN(this.MaxX))
                {
                    return true;
                }
                if (double.IsNaN(this.MinY) || double.IsNaN(this.MaxY))
                {
                    return true;
                }

                if (this.Width <= 0 || this.Height <= 0)
                {
                    return true;
                }
                return false;
            }
        }


        # endregion

        # region 公共方法

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ext"></param>
        /// <returns></returns>
        public bool Contains(IExtent ext)
        {
            if (this.Contains(new Coordinate(ext.MinX, ext.MinY)) == false) return false;
            if (this.Contains(new Coordinate(ext.MinX, ext.MaxY)) == false) return false;
            if (this.Contains(new Coordinate(ext.MaxX, ext.MinY)) == false) return false;
            if (this.Contains(new Coordinate(ext.MaxX, ext.MaxY)) == false) return false;

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public bool Contains(Coordinate pt)
        {
            if (pt.X < this._minX) return false;
            if (pt.X > this._maxX) return false;
            if (pt.Y < this._minY) return false;
            if (pt.Y > this._maxY) return false;

            return true; 
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="segment"></param>
        /// <returns></returns>
        public bool Contains(ISegmentShape segment)
        {
            if (this.Contains(segment.StartNode.Point) == false) return false;
            if (this.Contains(segment.StartNode.Point) == false) return false;
            foreach (IVertexShape vertex in segment.VertexCollection)
            {
                if (this.Contains(vertex.Point) == false) return false;
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ext"></param>
        /// <returns></returns>
        public bool Intersects(IExtent ext)
        {

            if (this._minX > ext.MaxX) return false;
            if (this.MaxX < ext.MinX) return false;
            if (this.MinY > ext.MaxY) return false;
            if (this.MaxY < ext.MinY) return false;

            return true;
        }


        public bool Intersects(IGeometry Geometry)
        {
            return (ToPolygon() as Polygon).Intersects(Geometry);
        }


        /// <summary>
        /// 合并
        /// </summary>
        /// <param name="extent"></param>
        /// <returns></returns>
        public IExtent Union(IExtent extent)
        {
            if (this.IsEmpty && extent.IsEmpty) return new Extent();
            if (this.IsEmpty) return extent;
            if (extent.IsEmpty) return this;

            return new Extent(Math.Min(this.MinX, extent.MinX), Math.Min(this.MinY, extent.MinY),
                Math.Max(this.MaxX, extent.MaxX), Math.Max(this.MaxY, extent.MaxY));
        }
              

        # endregion


        #region 静态方法

        /// <summary>
        /// 空Extent
        /// </summary>
        /// <returns></returns>
        public static IExtent Empty()
        {
            return new Extent(double.MaxValue, double.MaxValue, double.MinValue, double.MinValue);
        }

        #endregion


        public IExtent Copy()
        {
            IExtent ext = new Extent(this._minX, this._minY, this._maxX, this._maxY);
            return ext;
        }


        public IEnvelope ToEnvelope()
        {
            return new Envelope(_minX, _minY, _maxX, _maxY);
        }

        public IPolygon ToPolygon()
        {
            List<Coordinate> coors = new List<Coordinate>();
            coors.Add(new Coordinate(_minX, _minY));
            coors.Add(new Coordinate(_minX, _maxY));
            coors.Add(new Coordinate(_maxX, _maxY));
            coors.Add(new Coordinate(_maxX, _minY));
            coors.Add(coors[0]);
            IPolygon polygon = new Polygon(coors.ToArray());
            return polygon;
        }

    }
}
